#pragma once

#include <hiredis/hiredis.h>
#include <boost/date_time/gregorian/gregorian.hpp>

struct RedisLogin
{
    std::string host;
    uint16_t port;
    int db_index;
    std::string username;
    std::string passwd;
};

class RedisCpp
{
    class ReplyPtr
    {
    public:
        ReplyPtr(void* ptr)
        {
            ptr_ = (redisReply*)ptr;
        }
        ReplyPtr &operator=(void* ptr)
        {
            clean();
            ptr_ = (redisReply*)ptr;
            return *this;
        }
        ~ReplyPtr()
        {
            clean();
        }
        operator bool() const
        {
            return ptr_ != nullptr;
        }
        redisReply* operator->()
        {
            return ptr_;
        }

    private:
        redisReply *ptr_{nullptr};
    private:
        void clean() {
            if (ptr_) {
                freeReplyObject(ptr_);
                ptr_ = nullptr;
            }
        }
    };
public:
    RedisCpp(std::string_view host, uint16_t port, int db_index, const std::string &username = "", const std::string &passwd = "");
    RedisCpp(const RedisLogin &info);
    ~RedisCpp();
    const RedisLogin &login_info() const
    {
        return login_info_;
    }
    bool select(int index);
    bool set(std::string_view key, int v);
    bool set(std::string_view key, std::string_view v);
    bool set(std::string_view key, const std::string &v);
    bool set(std::string_view key, double v);
    bool set(std::string_view key, boost::gregorian::date);
    template <typename T>
    T get(std::string_view) const;
    bool flushdb();
private:
    redisContext *context_;
    const RedisLogin login_info_;
    // ReplyPtr reply_ptr_;
private:
};
